package com.adamrocker.android.sharefelica;

import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
import com.adamrocker.android.sharefelica.service.FelicaService;
import com.adamrocker.android.sharefelica.util.L;
import com.adamrocker.android.sharefelica.util.P;
import com.felicanetworks.mfc.AppInfo;
import com.felicanetworks.mfc.Felica;
import com.felicanetworks.mfc.FelicaEventListener;
import com.felicanetworks.mfc.FelicaException;
import com.felicanetworks.mfc.PushIntentSegment;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.TextView;
import static com.felicanetworks.mfc.FelicaException.ID_ILLEGAL_STATE_ERROR;
import static com.felicanetworks.mfc.FelicaException.ID_OPEN_ERROR;
import static com.felicanetworks.mfc.FelicaException.ID_IO_ERROR;
import static com.felicanetworks.mfc.FelicaException.ID_PERMISSION_ERROR;
import static com.felicanetworks.mfc.FelicaException.ID_UNKNOWN_ERROR;

/**
 * Felicaチップのopen/closeはActivityのonResume/onPauseに連動させる
 * 
 * @author 共有コミュニケ-ションチーム (adamrocker, BaoBab, tarotaro)
 */
public class ShareFelicaProxy extends Activity {
    private static final int MAX_URI_SIZE = 184;

    private static final CharSequence URI_HTTP = "http://";
    private static final CharSequence URI_HTTPS = "https://";
    private static final CharSequence URI_YOUTUBE = "http://m.youtube.com";
    private static final CharSequence URI_GOOGLE_MAP = "http://m.google.co.jp/u/m/";

    private static final int WHAT_START_COUNT_DOWN = 0;
    private static final int WHAT_COUNT_DOWN = 1;
    private static final int WHAT_FINISH = 2;
    private static final int WHAT_RETRY = 3;

    private static final int COUNT_DOWN_SEC = 3;
    private static final int RETRY_DELAY_MSEC = 3000;// msec
    private static final int MAX_RETRY_COUNT = 5;

    private TextView mText;
    private TextView mError;
    private ImageView mLogo;
    
    final private class MessageObj {
        final String title;
        final PushIntentSegment segment;
        private MessageObj (String t, PushIntentSegment s) {
            title = t;
            segment = s;
        }
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            int what = msg.what;
            switch (what) {
            case WHAT_START_COUNT_DOWN:
                startCountDown();// カウントダウンを開始
                break;
            case WHAT_COUNT_DOWN:
                int count = msg.arg1;
                countDown(count);// カウントダウンする
                break;
            case WHAT_FINISH:
                ShareFelicaProxy.this.finish();
                break;
            case WHAT_RETRY:
                MessageObj obj = (MessageObj) msg.obj;
                int retry = msg.arg1;
                push(obj.segment, obj.title, retry);
                break;
            }
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        L.i("onCreate");
        setContentView(R.layout.proxy);
        mText = (TextView) findViewById(R.id.msg_tv);
        mError = (TextView) findViewById(R.id.felica_error_tv);
        mLogo = (ImageView) findViewById(R.id.logo_img);

        Animation anim = AnimationUtils.loadAnimation(this, R.anim.blink);
        mText.setAnimation(anim);
        mLogo.setAnimation(anim);
        mError.setAnimation(anim);
    }

    /**
     * 共有する種類に応じてIntentを変更し、Felica通信で共有する。
     * 
     * @param it
     */
    private void handleIntent(Intent it) {
        L.i("handleIntent");
        final String act = it.getAction();
        if (Intent.ACTION_SEND.equals(act)) {
            String text = it.getStringExtra(Intent.EXTRA_TEXT);
            String title = it.getStringExtra(Intent.EXTRA_SUBJECT);
            title = title == null ? text : title;
            L.i(text);
            if (text.contains(URI_YOUTUBE)) {// YOUTUBEを共有
                pushUri(text, title);
            } else if (text.contains(URI_GOOGLE_MAP)) {// Google Mapsを共有
                String strMapUri = getMapUri(text);
                pushUri(strMapUri, title);
            } else if (text.contains(URI_HTTP) || text.contains(URI_HTTPS)) {// サイトを共有
                pushUri(text, title);
            }
        } else if (Intent.ACTION_DIAL.equals(act)) {// 電話番号を共有
            Uri uri = it.getData();
            pushDial(uri, uri.toString());
        } else if (Intent.ACTION_SENDTO.equals(act)) {// メールアドレスを共有
            Uri uri = it.getData();
            pushEmail(uri, uri.toString());
        }
    }

    private String getShortUri(String inputUri) {
        String shortUrl = ""; // 結果格納用文字列

        String longUrl = inputUri;
        String login = "felicathon";

        // TODO:apiKeyとloginアカウントは本来リポジトリにいれないほうがよい。リリース時には外すこと。
        String apiKey = "R_58dcf5d07461c8db28a1736549300b3c"; // apiKeyを記述

        // HTTP GET リクエスト文字列作成
        Uri.Builder uriBuilder = new Uri.Builder();
        uriBuilder.path("http://api.bit.ly/v3/shorten");
        uriBuilder.appendQueryParameter("longUrl", Uri.encode(longUrl));
        uriBuilder.appendQueryParameter("login", login);
        uriBuilder.appendQueryParameter("apiKey", apiKey);
        uriBuilder.appendQueryParameter("format", "txt");
        String uri = Uri.decode(uriBuilder.build().toString());

        try {
            // HTTP GET リクエスト
            HttpUriRequest httpGet = new HttpGet(uri);
            DefaultHttpClient defaultHttpClient = new DefaultHttpClient();
            HttpResponse httpResponse = defaultHttpClient.execute(httpGet);
            if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                // HTTP レスポンス
                String entity = EntityUtils.toString(httpResponse.getEntity());
                shortUrl = entity.replace("\n","");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return shortUrl;
    }

    private String getMapUri(String text) {
        String separator = "\n";
        return text.substring(text.indexOf(separator) + separator.length());

    }

    /**
     * この画面が表示されるとFelicaのactivateをONにする
     */
    public void onResume() {
        super.onResume();
        L.i("onResume");
        FelicaService service = FelicaService.getInstance();
        service.setContext(this, new FelicaEventListener() {
            @Override
            public void finished() {
                // activateに成功した時の処理
                L.d("FelicaEventListener#finished");
                stopAnimation();
                mHandler.sendEmptyMessage(WHAT_START_COUNT_DOWN);// Felica通信を開始するためのカウントダウンを開始
            }

            @Override
            public void errorOccurred(int arg0, String arg1, AppInfo arg2) {
                // activateに失敗した時の処理
                L.d("FelicaEventListener#errorOccured");
                showErrorAndFinish();
                showMessage("FelicaEventListener#errorOccurred(" + arg0 + ", "
                        + arg1);
            }
        });
        try {
            service.connectAndActivate();
        } catch (Exception e) {
            e.printStackTrace();
            String msg = e.getMessage();
            showMessage(msg);
        }
    }

    public void onPause() {
        super.onPause();
        L.i("onPause");
        // mHandler.removeMessages(FINISH);
        FelicaService service = FelicaService.getInstance();
        try {
            service.disconnectAndInactivate();
        } catch (Exception e) {
            e.printStackTrace();
            String msg = e.getMessage();
            showMessage(msg);
        }
    }

    private void animCountDown(final int count) {
        L.i("animCountDown");
        mText.setText(String.valueOf(count));
        Animation anim = AnimationUtils.loadAnimation(this, R.anim.scale);
        anim.setAnimationListener(new AnimationListener() {
            public void onAnimationStart(Animation animation) {
            }

            public void onAnimationRepeat(Animation animation) {
            }

            public void onAnimationEnd(Animation animation) {
                Message msg = new Message();
                msg.what = WHAT_COUNT_DOWN;
                msg.arg1 = count;
                mHandler.sendMessage(msg);// カウントのアニメーションが終わると次のカウントを開始
            }
        });
        mText.startAnimation(anim);
    }

    private void startCountDown() {
        L.i("startCountDown");
        animCountDown(COUNT_DOWN_SEC);
    }

    private void countDown(int oldCount) {
        L.i("countDown");
        int newCount = --oldCount;
        if (newCount < 0) {// カウントが０になったらFelica通信開始
            showLogo();
            Intent it = getIntent();
            handleIntent(it);
        } else {
            animCountDown(newCount);// カウントアニメーションを実行
        }
    }

    private void stopAnimation() {
        L.i("stopAnimation");
        mText.setAnimation(null);
    }

    private void showLogo() {
        mText.setVisibility(View.GONE);
        mError.setVisibility(View.GONE);
        Animation anim = AnimationUtils.loadAnimation(this, R.anim.blink);
        mLogo.setAnimation(anim);
        mLogo.setVisibility(View.VISIBLE);
    }

    private void showErrorAndFinish() {
        mText.setVisibility(View.GONE);
        mLogo.setVisibility(View.GONE);
        Animation anim = AnimationUtils
                .loadAnimation(this, R.anim.blink_cycle7);
        anim.setAnimationListener(new AnimationListener() {
            public void onAnimationStart(Animation animation) {
            }

            public void onAnimationRepeat(Animation animation) {
            }

            public void onAnimationEnd(Animation animation) {
                mHandler.sendEmptyMessage(WHAT_FINISH);// アニメーションが終了したらアプリも終了
            }
        });
        mError.setVisibility(View.VISIBLE);
    }

    /**
     * Felicaに通信して、そのログを保存する
     * 
     * @param segment
     * @throws FelicaException
     */
    private void pushAndSaveData(final PushIntentSegment segment, String title)
            throws FelicaException {
        Felica felica = FelicaService.getFelica();
        felica.open();
        felica.push(segment);
        felica.close();
        try {
            Intent it = segment.getIntentData();
            saveIntent(it, title);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void push(final PushIntentSegment segment, String title, final int retryCount) {
        L.i("push");
        if (retryCount < MAX_RETRY_COUNT) {
            try {
                pushAndSaveData(segment, title);// Felica通信
            } catch (FelicaException e) {
                switch (e.getID()) {
                case ID_ILLEGAL_STATE_ERROR:
                    L.d("ID_ILLEGAL_STATE_ERROR");
                    break;
                case ID_IO_ERROR:
                    L.d("ID_IO_ERROR");
                    break;
                case ID_OPEN_ERROR:
                    L.d("ID_OPEN_ERROR");
                    break;
                case ID_PERMISSION_ERROR:
                    L.d("ID_PERMISSION_ERROR");
                    break;
                case ID_UNKNOWN_ERROR:
                    L.d("ID_UNKNOWN_ERROR");
                    break;
                }
                // Felica通信に失敗したらリトライ
                int newRetryCount = retryCount + 1;
                retryHandler(segment, title, newRetryCount);
            }
        } else {
            showErrorAndFinish();
        }

    }

    private void retryHandler(final PushIntentSegment segment, String title,
            final int retryCount) {
        // Felica通信が成功するまでリトライするハンドラー（最大リトライ回数まで）
        Message msg = new Message();
        msg.what = WHAT_RETRY;
        msg.arg1 = retryCount;
        MessageObj obj = new MessageObj(title, segment);
        msg.obj = obj;
        mHandler.sendMessageDelayed(msg, RETRY_DELAY_MSEC);
    }

    private void pushIntent(Intent it, String title) {
        title = title != null ? title : ""; 
        //it.putExtra(P.TITLE, title); 文字量が大きくなるのでダメ
        PushIntentSegment seg = new PushIntentSegment(it);
        push(seg, title, 0);
    }

    private void pushUri(String uri, String title) {
        L.i(uri);
        String pushUri = uri;
        if (MAX_URI_SIZE < uri.length()) {
            pushUri = getShortUri(uri);
        }
        Intent it = new Intent(Intent.ACTION_VIEW, Uri.parse(pushUri));
        pushIntent(it, title);
    }

    private void pushDial(Uri uri, String title) {
        Intent it = new Intent(Intent.ACTION_DIAL, uri);
        pushIntent(it, title);
    }

    private void pushEmail(Uri uri, String title) {
        Intent it = new Intent(Intent.ACTION_SENDTO, uri);
        pushIntent(it, title);
    }

    private void saveIntent(Intent it, String title) throws Exception {
        FileOutputStream fo = openFileOutput(P.LOGFILE, MODE_APPEND);
        OutputStreamWriter osw = new OutputStreamWriter(fo);
        BufferedWriter bw = new BufferedWriter(osw);
        JSONObject json = new JSONObject();
        json.put(P.TITLE, title);
        json.put(P.ACTION, it.getAction());
        json.put(P.URI, it.getData().toString());
        String str = json.toString();
        bw.write(str + "\n");
        bw.close();
        osw.close();
        fo.close();
    }

    public void showMessage(String msg) {
        L.i("showMessage");
        mText.setText(msg);
    }
}